﻿/*----------------------------------------------------------------
 * 
 * 版权所有：Copyright (C) 2013
 * 
 * 文件名：Repository.cs
 * 文件功能描述：
 * 
 * 
 * 作 者：zhangq
 * 邮 箱：ifunnyface@outlook.com
 * 日 期：2013-01-23 09:18:08
 * 
 * 
 * 修改标识： Author
 * 修改描述： 相关描述
 * 
 * ---------------------------------------------------------------- */

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Common.Extensions.OrderBy;

namespace Infrastructure
{

    /// <summary>
    /// 操作仓抽象实现
    /// </summary>
    public abstract class RepositoryBase<T> : IRepository<T> where T : class
    {

        #region members

        /// <summary>
        /// DbContextFactory
        /// </summary>
        protected IDbContextFactory DbContextFactory { get; private set; }

        /// <summary>
        /// DBContext
        /// </summary>
        private EFDBContext dbContext;

        /// <summary>
        /// DBContext
        /// </summary>
        protected EFDBContext DataContext
        {
            get { return dbContext ?? (dbContext = DbContextFactory.GetDbContext()); }
        }


        /// <summary>
        /// DbSet
        /// </summary>
        protected readonly DbSet<T> dbSet;


        /// <summary>
        /// IUnitOfWork
        /// </summary>
        public IUnitOfWork UnitOfWork { get; set; }

        #endregion


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbContextFactory">IDbContextFactory</param>
        /// <param name="unitOfWork">IUnitOfWork</param>
        protected RepositoryBase(IDbContextFactory dbContextFactory, IUnitOfWork unitOfWork)
        {
            DbContextFactory = dbContextFactory;
            UnitOfWork = unitOfWork;
            dbSet = DataContext.Set<T>();
        }


        #region find one

        public virtual T Find(object id)
        {
            
            return dbSet.Find(id);
        }


        public virtual T Find(params object[] keys)
        {
            return dbSet.Find(keys);
        }


        public virtual T Find(Expression<Func<T, bool>> predicate, IList<string> includes=null)
        {
            var list = dbSet.Where(predicate);

            if (includes != null && includes.Any())
            {
                
                foreach (var item in includes)
                {
                    list = list.Include(item);
                }
            }
            return list.FirstOrDefault();
        }

        #endregion

        public virtual int Count(Expression<Func<T, bool>> predicate)
        {
            return dbSet.Count(predicate);
        }


        #region find by filter


        public virtual IEnumerable<T> LoadAll()
        {
            return dbSet.ToList();
        }




        public virtual IEnumerable<T> Filter(Expression<Func<T, bool>> predicate)
        {
            return Filter(predicate, null);
        }



        public virtual IEnumerable<T> Filter(Expression<Func<T, bool>> predicate,
                                             params IOrderByExpression<T>[] orderByExpressions)
        {
            var p = dbSet.Where(predicate);
            if (orderByExpressions != null)
            {
                p = p.OrderBy(orderByExpressions);
            }
            return p.ToList();
        }



        public virtual IEnumerable<T> Filter(Expression<Func<T, bool>> predicate,
                                             int pageIndex,
                                             int pageSize,
                                             params IOrderByExpression<T>[] orderByExpressions)
        {
            //var skip = pageIndex * pageSize;
            var list = predicate == null ? dbSet : dbSet.Where(predicate);

            if (orderByExpressions != null && orderByExpressions.Any())
            {
                list = list.OrderBy(orderByExpressions);
            }

            list = list.Skip((pageIndex - 1)*pageSize).Take(pageSize);

            //调试输出SQL，注意query不能ToList()，否则就不是IQueryable了 
            //Debug.WriteLine(list.ToString());

            return list.ToList();
        }


        public virtual IEnumerable<T> Filter(Expression<Func<T, bool>> predicate,
                                             int pageIndex,
                                             int pageSize,
                                             IList<string> includes, params IOrderByExpression<T>[] orderByExpressions)
        {
            var skip = pageIndex*pageSize;
            var list = predicate == null ? dbSet : dbSet.Where(predicate);
            if (includes != null && includes.Any())
            {
                foreach (var item in includes)
                {
                    list = list.Include(item);
                }
            }
            if (orderByExpressions != null && orderByExpressions.Any())
            {
                list = list.OrderBy(orderByExpressions);
            }

            list = skip == 0 ? list.Take(pageSize) : list.Skip(skip).Take(pageSize);

            //调试输出SQL，注意query不能ToList()，否则就不是IQueryable了 
            //Debug.WriteLine(list.ToString());

            return list.ToList();
        }

        #endregion


        public virtual bool Contains(Expression<Func<T, bool>> predicate)
        {
            return dbSet.Count(predicate) > 0;
        }


        #region CUD

        public virtual T Create(T entity)
        {
            var newEntry = dbSet.Add(entity);
            return newEntry;
        }

        public virtual void Delete(object id)
        {
            var entityToDelete = dbSet.Find(id);
            if (entityToDelete != null)
            {
                Delete(entityToDelete);
            }
        }

        public virtual void Delete(T entity)
        {
            if (dbContext.Entry(entity).State == EntityState.Detached)
            {
                dbSet.Attach(entity);
            }
            dbSet.Remove(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> predicate)
        {
            var entitiesToDelete = Filter(predicate);
            foreach (var entity in entitiesToDelete)
            {
                if (dbContext.Entry(entity).State == EntityState.Detached)
                {
                    dbSet.Attach(entity);
                }
                dbSet.Remove(entity);
            }
        }

        public virtual void Update(T entity)
        {
            var entry = dbContext.Entry(entity);
            dbSet.Attach(entity);
            entry.State = EntityState.Modified;
        }

        #endregion


    }

}